package com.xplay.xpocker.room.mahjong.util;

import lombok.Data;

import java.util.*;
import java.util.stream.Collectors;

public class MahjongHuUtil {
    /**
     * 整体逻辑 先移除 刻子  也就是一对
     * 然后再使用剩余的牌 进行 反复 计算
     * <p>
     * M*AAA + N*ABC + DD + 癞子
     *
     * @param cards    所有牌
     * @param variable 癞子
     * @param checkSz  是否检查顺子如果false  只检测   M*AAA + DD + 癞子
     * @return
     */
    public static boolean canHu(List<Integer> cards, int variable, boolean checkSz) {
        if ((cards.size() + variable + 1) % 3 != 0) {
            return false;
        }
        cards.sort(Integer::compareTo);
        //7对校验 注意这里深度拷贝 不要影响原来的数组
        if (cards.size() + variable == 14 && checkPokerAA(new ArrayList<>(cards), variable)) {
            return true;
        }
        // 逆序剔除
        for (int i = cards.size() - 1, j = 0; i >= j; i--) {
            // 当执行第二次循环的时候发现当前数据与上一个数据一样就直接跳过
            if (i < cards.size() - 1 && Objects.equals(cards.get(i), cards.get(i + 1))) {
                continue;
            }
            // 移出两个元素
            if ((i >= 1 && cards.get(i).equals(cards.get(i - 1))) || variable > 0) {
                // 深度拷贝一份 用于胡牌计算 如果不满足继续循环重新移出 也不需要追加回去
                List<Integer> puCards = cards.stream().map(Integer::new).collect(Collectors.toList());
                int variableClone = variable;
                // 按照位置移出
                puCards.remove(i);
                // 如果有一样的数据  就直接用
                if (i >= 1 && puCards.get(i - 1).equals(cards.get(i))) {
                    puCards.remove(i - 1);
                } else {
                    // 没有就用掉一个癞子
                    variableClone--;
                }
                if (isPu(puCards, variableClone, checkSz)) {
                    return true;
                }
            }
        }
        if (variable >= 2 && isPu(cards, variable - 2, checkSz)) {
            // 两个癞子做将牌
            return true;
        }
        return false;
    }

    public static boolean isPu(List<Integer> cards, int variable, boolean checkSz) {
        if (cards.size() == 0) {
            return true;
        }
        if (checkSz) {
            // 减去2的原因是   如果是顺子 第一张 一定是 小于等于7
            for (int first = cards.get(0) - 2; first <= cards.get(0); first++) {
                if (first % 10 > 7 || (variable == 0 && first < cards.get(0))) {
                    continue;
                }
                int shunCount = 0;
                for (int i = 0; i < 3; i++) {
                    if (cards.contains(first + i)) {
                        shunCount++;
                    }
                }
                if (shunCount == 3 || shunCount + variable >= 3) {
                    // 深度拷贝一份 用于胡牌计算 如果不满足继续循环重新移出 也不需要追加回去
                    List<Integer> puCards = cards.stream().map(Integer::new).collect(Collectors.toList());
                    int variableClone = variable;
                    for (int i = 0; i < 3; i++) {
                        int deletePos = puCards.indexOf(first + i);
                        if (deletePos >= 0) {
                            puCards.remove(deletePos);
                        } else {
                            variableClone--;
                        }
                    }
                    if (isPu(puCards, variableClone, checkSz)) {
                        return true;
                    }
                }
            }
        }

        // 如果最后只剩下两张牌和一张癞子
        if (cards.size() == 2 && cards.get(0).equals(cards.get(1)) && variable == 1) {
            return true;
        } else if (cards.size() == 2 && !cards.get(0).equals(cards.get(1)) && variable == 1) {
            return false;
        } else if (cards.size() == 1 && variable == 2) {
            // 如果最后只剩下一张牌 和两张癞子
            return true;
            // 如果最后只剩下一张牌和非两张癞子的情况
        } else if (cards.size() == 1) {
            return false;
        }
        int keziCount = 1;
        Integer keziCard = cards.get(0);
        if (Objects.equals(cards.get(1), keziCard)) {
            keziCount++;
        }
        if (Objects.equals(cards.get(2), keziCard)) {
            keziCount++;
        }
        if (keziCount == 3 || keziCount + variable >= 3) {
            // 深度拷贝一份 用于胡牌计算 如果不满足继续循环重新移出 也不需要追加回去
            List<Integer> puCards = cards.stream().map(Integer::new).collect(Collectors.toList());
            int variableClone = variable;
            for (int i = 0; i < 3; i++) {
                int deletePos = puCards.indexOf(keziCard);
                if (deletePos >= 0) {
                    puCards.remove(deletePos);
                } else {
                    variableClone--;
                }
            }
            if (isPu(puCards, variableClone, checkSz)) {
                return true;
            }
        }
        return false;
    }


    /**
     * N * DD + 癞子
     *
     * @param list
     * @param variable
     * @return
     */

    public static boolean checkPokerAA(List<Integer> list, int variable) {
        int totalCardCount = list.size() + variable; // 牌的总数量
        if (totalCardCount % 2 != 0 || list.size() + variable != 14) {
            return false; // 如果牌的数量不是偶数，肯定无法组成七对胡牌
        }
        Map<Integer, Integer> countMap = new HashMap<>();
        for (Integer card : list) {
            countMap.put(card, countMap.getOrDefault(card, 0) + 1);
        }
        if (countMap.size() > 7) {
            return false;
        }
        int oddCount = 0; // 记录出现奇数次的牌的数量
        for (int count : countMap.values()) {
            if (count % 2 != 0) {
                oddCount++;
            }
        }
        return oddCount <= variable; // 如果出现奇数次的牌的数量小于等于癞子的数量，则可以胡牌
    }

    /**
     * 是否满足 大队
     * M*AAA + DD
     *
     * @param list
     * @param variable
     * @return
     */


    public static boolean checkPokerAAA(List<Integer> list, int variable) {
        int totalCardCount = list.size() + variable;
        if (totalCardCount % 3 != 2) {
            return false;
        }
        if (list.size() == 2 || list.size() + variable == 2) {
            return variable > 1 || list.get(0).equals(list.get(1));
        }
        Map<Integer, Integer> countMap = new HashMap<>();
        for (Integer card : list) {
            countMap.put(card, countMap.getOrDefault(card, 0) + 1);
        }
        int singleCount = 0; // 记录出现奇数次的牌的数量
        for (int count : countMap.values()) {
            if (count % 3 == 1) {
                singleCount++;
            }
        }
        return singleCount <= variable; // 如果出现奇数次的牌的数量小于等于癞子的数量，则可以胡牌
    }

    /**
     * 判断是否存在胡牌
     *
     * @param allCard
     */
    public static List<Integer> checkHInfo(List<Integer> allCard, int variable) {
        List<Integer> allCardTemp = new ArrayList<>(allCard);
        List<Integer> preSelections = new ArrayList<>();
        int maxRedundant = (variable > 0) ? variable + 2 : 1;
        for (int card : allCardTemp) {
            int min = 1, max = 9;
            int maxRedundantTemp = maxRedundant;
            if (card > 10 && card < 20) {
                min = 11;
                max = 19;
            } else if (card > 20 && card < 30) {
                min = 20;
                max = 29;
            }
            int cardTemp = card;
            while (cardTemp >= min && maxRedundantTemp >= 0) {
                if (!preSelections.contains(cardTemp)) {
                    preSelections.add(cardTemp);
                }
                cardTemp--;
                maxRedundantTemp--;
            }
            maxRedundantTemp = maxRedundant;
            cardTemp = card;
            while (cardTemp <= max && maxRedundantTemp >= 0) {
                if (!preSelections.contains(cardTemp)) {
                    preSelections.add(cardTemp);
                }
                cardTemp++;
                maxRedundantTemp--;
            }
        }
        List<Integer> result = new ArrayList<>();
        for (int element : preSelections) {
            List<Integer> tempList = new ArrayList<>(allCard);
            tempList.add(element);
            if (canHu(tempList, variable, true)) {
                result.add(element);
            }
        }
        return result;
    }

    public static List<CardEHInfo> checkEHInfo(List<Integer> allCard, int variable) {
        List<CardEHInfo> result = new ArrayList<>();
        int size = allCard.size() + variable;
        int index = 0;
        int exportVariable = 0;
        if (size % 3 == 2) {
            while (index < size) {
                if (allCard.size() > index) {
                    // 移出一张卡牌
                    List<Integer> allCardTemp = new ArrayList<>(allCard);
                    Integer exportCard = allCardTemp.get(index);
                    allCardTemp.remove(index);
                    // 计算胡牌
                    List<Integer> huCards = checkHInfo(allCardTemp, variable);
                    if (huCards.size() > 0) {
                        result.add(new CardEHInfo(exportCard, huCards));
                    }
                } else {
                    exportVariable++;
                    // 移出一张卡牌
                    List<Integer> allCardTemp = new ArrayList<>(allCard);
                    // 计算胡牌
                    List<Integer> huCards = checkHInfo(allCardTemp, variable - exportVariable);
                    if (huCards.size() > 0) {
                        result.add(new CardEHInfo(1, huCards));
                    }
                }
                index++;
            }
        } else if (size % 3 == 1) {
            List<Integer> huCards = checkHInfo(new ArrayList<>(allCard), variable);
            if (huCards.size() > 0) {
                result.add(new CardEHInfo(null, huCards));
            }
        }
        return result;
    }

    public static void main(String[] args) {
        int[] allCard = {12, 12, 12, 12, 14, 14, 15, 15, 16, 16, 17, 17, 19, 19};
        List<Integer> allCardList = Arrays.stream(allCard).boxed().collect(Collectors.toList());
        System.out.println(checkPokerAA(allCardList, 0));
    }


    @Data
    public static class CardEHInfo {
        Integer exportCard;
        List<Integer> huCards;

        public CardEHInfo(Integer exportCard, List<Integer> huCards) {
            this.exportCard = exportCard;
            this.huCards = huCards;
        }

        @Override
        public String toString() {
            return "{出牌:" + exportCard + ", 胡牌:" + huCards + "}";
        }
    }
}
